package com.atguigu.tingshu.common.aspect;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.anno.GuiGuLogin;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class GuiGuLoginAspect {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 登录认证切面方法。
     *
     * @SneakyThrows 注解用于自动抛出检查型异常，避免在方法签名中显式声明异常。
     * @Around 注解表示该方法是一个环绕增强，即在目标方法执行前后都会执行该方法。
     *         "execution(* com.atguigu.tingshu.*.api.*.*(..))" 指定了切点表达式，匹配 com.atguigu.tingshu 包下任意子包中 api 子包内所有类的所有方法。
     *         "@annotation(guiGuLogin)" 表示该方法仅对标注了 @GuiGuLogin 注解的方法进行增强。
     *
     * @param joinPoint 切面连接点，提供了访问目标方法执行前后的能力。
     * @param guiGuLogin 自定义的 @GuiGuLogin 注解实例，用于配置登录认证的要求。
     * @return 目标方法的返回值，如果目标方法正常执行则返回其返回值，如果发生异常则返回 null。
     * @throws Throwable 如果在环绕增强的过程中抛出异常，则会被捕获并打印堆栈信息，但不会被向上抛出。
     */
    @SneakyThrows
    @Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(guiGuLogin)")
    public Object loginAspect(ProceedingJoinPoint joinPoint, GuiGuLogin guiGuLogin) {

        // 获取当前请求的RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("token");

        // 判断是否需要登录认证
        if (guiGuLogin.required()) {

            // 如果token为空，则抛出未登录异常
            if (StringUtils.isEmpty(token)) {
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }

            // 从Redis中获取用户信息
            String userInfoJSON = this.redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
            // 如果用户信息为空，则抛出未登录异常
            if (StringUtils.isEmpty(userInfoJSON)) {
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }

            // 将用户信息解析为UserInfo对象
            UserInfo userInfo = JSON.parseObject(userInfoJSON, UserInfo.class);
            // 将用户ID和用户名保存到AuthContextHolder中
            AuthContextHolder.setUserId(userInfo.getId());
            AuthContextHolder.setUsername(userInfo.getNickname());

        } else {

            // 如果不需要登录认证，但token不为空，则尝试从Redis中获取用户信息
            if (!StringUtils.isEmpty(token)) {
                String userInfoJSON = this.redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
                if (!StringUtils.isEmpty(userInfoJSON)) {
                    UserInfo userInfo = JSON.parseObject(userInfoJSON, UserInfo.class);
                    // 将用户名和用户ID保存到AuthContextHolder中
                    AuthContextHolder.setUsername(userInfo.getNickname());
                    AuthContextHolder.setUserId(userInfo.getId());
                }
            }

        }

        try {
            // 执行目标方法
            Object proceed = joinPoint.proceed();

            // 返回目标方法的返回值
            return proceed;

        } catch (Throwable e) {
            // 捕获异常并打印堆栈信息
            e.printStackTrace();
        } finally {
            // 无论是否发生异常，都移除AuthContextHolder中的用户ID和用户名
            AuthContextHolder.removeUserId();
            AuthContextHolder.removeUsername();
        }

        // 如果在try-catch块中发生了异常，则返回null
        return null;

    }

}
